import java.util.ArrayList;
import java.util.List;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: 25397
 * Date: 2021-12-03
 * Time: 13:43
 */
class MyArray<T> {
    //泛型数组
    //public T[] objects=new T[10];//会报错，泛型不可以new数组，原因见MyArrayList类里面说明

    public T[] array=(T[])new Object[10];//这样写也不可以，这样数组里面可以放任意类型数据

    //正确的创建泛型数组方法：反射
    //public MyArray(Class<T> clazz,int capacity){
    // array=(T[])Array.newInstance(clazz,capacity);
    //}

    public T getPos(int pos){
        return this.array[pos];
    }

    public void setVal(int pos,T val){
        this.array[pos]=val;
    }


}
public class TestDemo {
    /*public static void main(String[] args) {
        MyArrayList myArrayList=new MyArrayList();
        myArrayList.add(1);
        myArrayList.add(2);
        int x= myArrayList.getVal(0);
        int y= myArrayList.getVal(1);
        System.out.println(x);
        System.out.println(y);
    }*///这种顺序表只能添加整形类，如果我想要可以添加其他类型数据的顺序表怎么办？


    /*public static void main(String[] args) {
        MyArrayList myArrayList=new MyArrayList();
        myArrayList.add(1);
        myArrayList.add("hello");
        int x=(int)myArrayList.getVal(0);
        String y=(String)myArrayList.getVal(1);
        System.out.println(myArrayList.elem[0]);
        System.out.println(myArrayList.elem[1]);
    }*///这种方法可以实现一个顺序表中添加不同类型的数据，但是每次拿出来都要强转非常麻烦

    /*public static void main(String[] args) {
        MyArrayList myArrayList=new MyArrayList();
        myArrayList.add(1);
        myArrayList.add("hhh");
        int x=(int)myArrayList.elem[0];//这里我们会发现，我们还是用的强转，那到底怎么改进呢？
        System.out.println(x);
    }*/

    /*public static void main(String[] args) {
        MyArrayList<Integer> myArrayListInt=new MyArrayList<Integer>();
        //MyArrayList后面给一个<Integer>，表示你当前要存放什么类型的数据,这个是必须要有的，不然编译器不知道你要用什么类型，怎么给你分配内存空间
        //第二个Integer是灰色的，表示你写不写都随便，没什么影响
        //那<>里放int可以吗？char呢？float呢？
        //简单类型不能作为泛型类型的参数，比如int、char、float


        myArrayListInt.add(1);
        myArrayListInt.add(2);
        int x= myArrayListInt.getVal(0);
        int y= myArrayListInt.getVal(1);
        System.out.println(x);//打印1
        System.out.println(y);//打印2
        //myArrayListInt.add("hhh");//如果我们想添加字符串类型的，就不行了

        MyArrayList<String> myArrayListString=new MyArrayList<>();
        myArrayListString.add("hhh");

    }*/
    //泛型使用范例如下：（MyArray类在开头）
    /*public static void main(String[] args) {
        MyArray<Integer>myArray=new MyArray<>();
        myArray.setVal(0,10);
        myArray.setVal(1,11);
        int ret= myArray.getPos(1);
        System.out.println(ret);//打印11
    }*/


    //裸类型
    /*public static void main(String[] args) {
        MyArray myArray=new MyArray();//不加<>就是裸类型
        myArray.setVal(0,"hello");
        myArray.setVal(1,520);//因为是裸类型，所以往里面加元素没有限制
        //这种裸类型-泛型没有起作用
    }*/


    //注意事项
    /*public static void main(String[] args) {
        ArrayList<Integer> arrayList=new ArrayList<>();
        arrayList.add(1);
        arrayList.add(2);
        arrayList.add(3);
        //Integer[] ret=arrayList.toArray();//这样写会报错
        Object[] ret=arrayList.toArray();//这样写不会报错
        //toArray方法返回的是Object[]类型
    }*/


    //泛型上界：
    //在定义泛型类时，有时需要对传入的类型变量做一定的约束，可以通过类型边界来约束
    class MyArr<E extends Number>{
        //E只可以是Number或者Number的子类
    }

    /*public static void main(String[] args) {
        MyArr<Integer> myArr1;//不报错
        //MyArr<String> myArr2;//报错，String不是Number子类
    }*/
    //ps:泛型只有上界，无下界


    //写一个泛型类，求出数组中最大值
    /*class Alg<T>{
        public T findMax(T[]array){
            T max=array[0];
            for(int i=0;i<array.length;i++){
                if(max<array[i]){
                    //这里会报错：泛型的参数（就是<>里的东西）都是引用类型
                    //引用类型不能用大于小于号比较
                    max=array[i];
                }
            }
            return max;
        }
    }*/

    //正确写法：
    static class Alg<T extends Comparable>{//加static是因为该类在TestDemo类里面，如果在外面可以不加
        //传过来的泛型类（T）必须是Comparable的子类或Comparable
        public T findMax(T[]array){
            T max=array[0];
            for(int i=0;i<array.length;i++){
                if(max.compareTo(array[i])<0){
                    max=array[i];
                }
            }
            return max;
        }
    }

    /*public static void main(String[] args) {
        Alg<Integer> alg1=new Alg<>();
        Integer[]arr={1,21,32,4};
        System.out.println(alg1.findMax(arr));//打印32

        Alg<String> alg2=new Alg<>();
        String[]arr2={"abc","hello","hi"};
        System.out.println(alg2.findMax(arr2));//打印hi
    }*/

    //泛型中的父子类关系
    public static class MyArrayList<E>{
        //MyArrayList<Object>不是MyArrayList<Number>的父类
        //MyArrayList<Number>也不是MyArrayList<Integer>的父类
        //原因：在编译过程中，<>里的东西都被擦除成了Object
    }

    //通配符？（问号就是通配符）
    //用于解决泛型无法协变的问题，协变就是指如果Student是Person的子类，
    //那么List<Student>也应该是List<Person>的子类，但是泛型是不支持这样的父子类关系的

    //1.泛型T是确定的类型，一旦你传了，就定下来了，而通配符更为灵活
    //2.或者我们这样理解：泛型T就是一个变量，等待你传一个具体的类型，而通配符则是一种规定，你只能传哪些参数
    //示例1
    public static <T> void printList(ArrayList<T> list){
        for(T x:list){
            System.out.println(x);
        }
    }//此时示例1参数是T，T一定是指定的某个泛型参数

    //示例2
    public static void printList2(ArrayList<?>list){
        for(Object x:list){//?是任何类型，我们只能通过Object来接收
            System.out.println(x);
        }
    }//示例2使用了通配符，与示例1相比，
    // 此时传入printList2的，具体是什么类型我们是不清楚的，这就是通配符

    //通配符上界
    //语法：<？extends 上界>
    public static void printAll(MyArrayList<? extends Number> list){
        //...
    }

    /*public static void main(String[] args) {
        //下面的调用都是正确的
        printAll(new MyArrayList<Integer>());
        printAll(new MyArrayList<Double>());
        printAll(new MyArrayList<Number>());

        //下面调用，编译会报错
        //printAll(new MyArrayList<String>());
        //printAll(new MyArrayList<Object>());
    }*/

    //通配符的父子类关系
    //MyArrayList<?extends Number>是MyArrayList<Integer>或者MyArrayList<Double>的父亲类型
    //MyArrayList<?>是MyArrayList<?extends Number>的父类型

    /*public static void main(String[] args) {
        ArrayList<Integer> arrayList1=new ArrayList<>();
        List<? extends Number> list=arrayList1;

        //通配符的上界不适合写入数据
        //list.add(0,1);//这里会报错
        //list.add(1,1.1);//这里也会报错
        //这里可以引用Integer的list，也可以用Double的list
        //编译器不知道你到底引用哪个


        //通配符的上界适合读取数据
        Number o=list.get(0);
        //因为你list里面都是Number或Number的子类
        //你可以用Number一个来接收
        //Integer a=list.get(1);//这里会报错，因为Number的子类很多，你不能确定在1号位置就是Integer类型
    }*/


    //通配符下界
    //语法：<?super 下界>
    //eg:<?super Integer>代表可以传入的实参类型是Integer或Integer的父类类型
    //<?>是<?super Integer>的父类类型，这里<?>就相当于是Object了
    public static void main(String[] args) {
        ArrayList<?super Person> arrayList=new ArrayList<Person>();//new ArrayList这里<>里面Person是默认的，可以不写
        //这里arrayList里面都是Person或Person的父类
        //ArrayList<?super Person> arrayList2=new ArrayList<student>();//<>里面放student会报错，只能放Person或Person的父类
        arrayList.add(new Person());
        arrayList.add(new student());//这里不会报错
        //添加元素时，只要添加的元素是Person或Person的子类即可
        //arrayList.add(new A());//不能添加Person的父类


        //为什么呢？
        //因为添加元素的时候，我们知道list引用的对象肯定是Person或Person父类的集合
        //我们保证添加的元素比Person小，或者就是Person就可以（向上转型）
        //ps:Person的父类也不能添加

        //student person=arrayList.get(0);//这里会报错，读取的时候，你不知道读的是哪个子类
        //Person person=arrayList.get(0);//这里也会报错
        Object o=arrayList.get(0);//只能Object接收



    }


}

class A{

}
class Person extends A{

}
class student extends Person{

}